package core.proto.vis.action;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;

import javax.swing.DefaultListModel;
import javax.swing.SwingUtilities;

import prefuse.Visualization;
import prefuse.action.GroupAction;
import prefuse.data.Graph;
import prefuse.data.Node;
import prefuse.data.tuple.TupleSet;
import prefuse.visual.EdgeItem;
import prefuse.visual.NodeItem;
import core.proto.vis.VizDemoRadial;
import core.proto.vis.action.util.Position;
import core.util.logging.UserLogWriter;
import core.util.logging.constants.Action;
import core.util.logging.constants.Location;

/**
 * Switch the root of the tree by requesting a new spanning tree at the desired
 * root
 * 
 * @author Bill
 */
public class TreeRootAction extends GroupAction
{
	private Map<NodeItem, Position> positions;

	private Map<String, List<EdgeItem>> attrMapping;

	private List<EdgeItem> lastHighlighted;

	private DefaultListModel listData;

	public TreeRootAction( String graphGroup,
			Map<NodeItem, Position> positions, DefaultListModel listData,
			Map<String, List<EdgeItem>> attrMapping ) {
		super( graphGroup );
		this.positions = positions;
		this.listData = listData;
		this.attrMapping = attrMapping;
		this.lastHighlighted = lastHighlighted;
	}

	public void run( double frac ) {
		attrMapping.clear();

		// set every edge to unhighlighted
		TupleSet edges = m_vis.getGroup( "tree.edges" );
		Iterator it = edges.tuples();
		while( it.hasNext() ) {
			Object next = it.next();
			if( next instanceof EdgeItem ) {
				((EdgeItem) next).setHighlighted( false );
			}
		}

		TupleSet focus = m_vis.getGroup( Visualization.FOCUS_ITEMS );
		if( focus == null || focus.getTupleCount() == 0 )
			return;

		Graph g = (Graph) m_vis.getGroup( m_group );

		Node f = null;
		Object o = null;
		@SuppressWarnings("rawtypes")
		Iterator tuples = focus.tuples();
		while( tuples.hasNext() ) {
			o = tuples.next();
			// if o is not a node or it's not part of the graph
			if( !(o instanceof Node) || !g.containsTuple( f = (Node) o ) ) {
				return;
			}

//			UserLogWriter.log( "Node: " + f.get( "name" ), Location.Viz, Action.CLICK.toString());
			g.getSpanningTree( f );

			// re-locate the locked nodes
			for( NodeItem n : positions.keySet() ) {
				Position p = positions.get( n );
				n.setX( p.getX() );
				n.setY( p.getY() );
			}

			// we want all of the attributes, non-dupe
			final Set<String> attributes = new HashSet<String>();
			String attribute;

			TupleSet edgeSet = m_vis.getGroup( VizDemoRadial.BLUE_EDGE_SET );
			Iterator inIter = f.inEdges();
			Iterator outIter = f.outEdges();
			edgeSet.clear();
			while( inIter.hasNext() ) {
				EdgeItem edge = (EdgeItem) inIter.next();
				edgeSet.addTuple( edge );
				updateAttributeSet( attributes, attrMapping, edge );
			}
			while( outIter.hasNext() ) {
				EdgeItem edge = (EdgeItem) outIter.next();
				edgeSet.addTuple( edge );
				updateAttributeSet( attributes, attrMapping, edge );
			}

			Iterator nodeIter = f.inNeighbors();
			while( nodeIter.hasNext() ) {
				NodeItem n = (NodeItem) nodeIter.next();
				n.setHighlighted( true );
			}

			SwingUtilities.invokeLater((new Runnable(){
				public void run(){
					listData.clear();
					for( String attr : attributes ) {
						listData.addElement( attr );
					}
				}
			}));

		}
	}

	private static void updateAttributeSet( Set<String> attributes,
			Map<String, List<EdgeItem>> attrMapping, EdgeItem edge ) {
		String attribute;
		if( edge.canGetString( "name" ) ) {
			attribute = edge.getString( "name" );
			StringTokenizer st = new StringTokenizer( attribute, ", \t\n" );
			while( st.hasMoreElements() ) {
				String attr = st.nextElement().toString().trim();
				attributes.add( attr );
				if( attrMapping.containsKey( attr ) ) {
					attrMapping.get( attr ).add( edge );
				} else {
					List<EdgeItem> edges = new ArrayList<EdgeItem>();
					edges.add( edge );
					attrMapping.put( attr, edges );
				}
			}
		}
	}
}
